![]() |
AVR675 Configurable Three Phase Fan
2.0
|
BLDC Sensorless Control using the ATtiny461A for Fan control.
#include <avr/io.h>#include <avr/interrupt.h>#include <avr/eeprom.h>Go to the source code of this file.
BLDC Sensorless Control using the ATtiny461A for Fan control.
Definition in file main.c.
| #define ADC_PRESCALER 4 |
| #define bemf variable_table[8] |
| #define bemf_error variable_table[9] |
Definition at line 99 of file main.c.
Referenced by test_oscillator().
| #define CMD_PIN_1 0 |
Definition at line 100 of file main.c.
Referenced by ISR(), and test_oscillator().
| #define CMD_PIN_TEST (PINA & (1<<CMD_PIN)) |
Definition at line 98 of file main.c.
Referenced by ISR(), and test_oscillator().
| #define command variable_table[0] |
| #define command_0 variable_table[24 + VARIABLE_TABLE_SIZE] |
| #define command_1 variable_table[26 + VARIABLE_TABLE_SIZE] |
| #define command_2 variable_table[28 + VARIABLE_TABLE_SIZE] |
| #define command_3 variable_table[30 + VARIABLE_TABLE_SIZE] |
| #define counter variable_table[15] |
| #define current variable_table[5] |
| #define current_limit variable_table[4] |
| #define current_limit_max variable_table[21 + VARIABLE_TABLE_SIZE] |
| #define current_limit_slope variable_table[22 + VARIABLE_TABLE_SIZE] |
| #define current_limit_startup variable_table[20 + VARIABLE_TABLE_SIZE] |
| #define data_rxd variable_table[14] |
| #define data_watch variable_table[2 + VARIABLE_TABLE_SIZE] |
| #define EDGE_FALLING 1 |
| #define EDGE_RISING 0 |
| #define EEPROM_TABLE_SIZE 32 |
Definition at line 131 of file main.c.
Referenced by ISR(), main(), read_eeprom(), and write_eeprom().
| #define enable_voltage variable_table[0 + VARIABLE_TABLE_SIZE] |
| #define HALF_LIMIT 0x7FFFu |
| #define HEADER_SIZE 64 |
| #define input_cmd variable_table[4 + VARIABLE_TABLE_SIZE] |
| #define ki variable_table[9 + VARIABLE_TABLE_SIZE] |
| #define kp variable_table[8 + VARIABLE_TABLE_SIZE] |
| #define LED_PIN PB6 |
Definition at line 114 of file main.c.
Referenced by intLEDPin().
| #define LED_PIN_TOGGLE PORTB ^= (1<<LED_PIN) |
| #define LOOP_SCALING 3u |
| #define LOWER_DRIVE_STEP1_CW (1 << WL) |
| #define LOWER_DRIVE_STEP2_CW (1 << WL) |
| #define LOWER_DRIVE_STEP3_CW (1 << VL) |
| #define LOWER_DRIVE_STEP4_CW (1 << VL) |
| #define LOWER_DRIVE_STEP5_CW (1 << UL) |
| #define LOWER_DRIVE_STEP6_CW (1 << UL) |
| #define mode variable_table[3 + VARIABLE_TABLE_SIZE] |
| #define osc_cal variable_table[12] |
| #define osc_cal_adj variable_table[1 + VARIABLE_TABLE_SIZE] |
| #define osc_error variable_table[13] |
Definition at line 320 of file main.c.
Referenced by test_oscillator().
| #define pll_gravity variable_table[10 + VARIABLE_TABLE_SIZE] |
| #define pwm variable_table[7] |
| #define pwm_limit variable_table[6] |
| #define pwm_max variable_table[12 + VARIABLE_TABLE_SIZE] |
| #define pwm_min variable_table[11 + VARIABLE_TABLE_SIZE] |
| #define r0_limit variable_table[7 + VARIABLE_TABLE_SIZE] |
| #define s_ki variable_table[16 + VARIABLE_TABLE_SIZE] |
| #define s_kp variable_table[15 + VARIABLE_TABLE_SIZE] |
| #define s_loop_rate variable_table[13 + VARIABLE_TABLE_SIZE] |
| #define s_r0_limit variable_table[14 + VARIABLE_TABLE_SIZE] |
| #define speed variable_table[2] |
| #define speed_cmd variable_table[1] |
| #define speed_cmd_0 variable_table[25 + VARIABLE_TABLE_SIZE] |
| #define speed_cmd_1 variable_table[27 + VARIABLE_TABLE_SIZE] |
| #define speed_cmd_2 variable_table[29 + VARIABLE_TABLE_SIZE] |
| #define speed_cmd_3 variable_table[31 + VARIABLE_TABLE_SIZE] |
| #define speed_cmd_max variable_table[18 + VARIABLE_TABLE_SIZE] |
| #define speed_cmd_min variable_table[17 + VARIABLE_TABLE_SIZE] |
| #define speed_count_max variable_table[6 + VARIABLE_TABLE_SIZE] |
| #define speed_count_min variable_table[5 + VARIABLE_TABLE_SIZE] |
| #define speed_error variable_table[3] |
| #define speed_scale variable_table[19 + VARIABLE_TABLE_SIZE] |
| #define TACH_PIN_0 PORTA |= (1<<TACH_PIN) |
| #define TACH_PIN_1 PORTA &= ~(1<<TACH_PIN) |
| #define TEST_PIN PA3 |
Definition at line 109 of file main.c.
Referenced by intTestPin().
| #define TEST_PIN_0 PORTA &= ~(1<<TEST_PIN) |
| #define TEST_PIN_1 PORTA |= (1<<TEST_PIN) |
Definition at line 111 of file main.c.
Referenced by ISR(), and test_oscillator().
| #define UPPER_DRIVE_MASK ((1 << UH) | (1 << VH) | (1 << WH)) |
| #define UPPER_DRIVE_STEP1_CW (1 << VH) |
| #define UPPER_DRIVE_STEP2_CW (1 << UH) |
| #define UPPER_DRIVE_STEP3_CW (1 << UH) |
| #define UPPER_DRIVE_STEP4_CW (1 << WH) |
| #define UPPER_DRIVE_STEP5_CW (1 << WH) |
| #define UPPER_DRIVE_STEP6_CW (1 << VH) |
| #define v_bus variable_table[10] |
| #define v_bus_min variable_table[23 + VARIABLE_TABLE_SIZE] |
| #define v_motor variable_table[11] |
| #define VARIABLE_TABLE_SIZE 16 |
Definition at line 128 of file main.c.
Referenced by ISR(), main(), read_eeprom(), and write_eeprom().
| void intADC | ( | void | ) |
Initializes ADC.
ADC is initialized in single ended mode, Left Aligned for 8 bit values Using VCC as the reference. The conversion is triggered by the timer 1 overflow
Definition at line 501 of file main.c.
References ADC_MUX_I, and ADC_PRESCALER.
Referenced by main().
{
//PA1(ADC1)-DIR,PA5(ADC4)-THR,PA6(ADC5)-IB,PA7(ADC6)-IA,
//PB6(ADC9)-V+,PB7(ADC10)-TEMP
//ADMUX – ADC Multiplexer Selection Register
//Bits 7:6 – REFS1:REFS0: Voltage Reference Selection Bits 00= Vcc
//Bit 5 – ADLAR: ADC Left Adjust Result
//Bits 4:0 – MUX4:0: Analog Channel and Gain Selection Bits
ADMUX = ADC_MUX_I;
//ADCSRA – ADC Control and Status Register A
//Bit 7 – ADEN: ADC Enable
//Bit 6 – ADSC: ADC Start Conversion
//Bit 5 – ADATE: ADC Auto Trigger Enable
//Bits 2:0 – ADPS2:0: ADC Prescaler Select Bits
ADCSRA = ((1<<ADEN) | (1<<ADATE) | ADC_PRESCALER);
//ADCL an ADCH – The ADC Data Register
//read just ADCH for 8 bit values
//ADCSRB – ADC Control and Status Register B
//Timer/Counter1 Overflow - ADTS2 = 1, ADTS1 = 1, ADTS0 = 0
ADCSRB = ((1<<ADTS2) | (1<<ADTS1));
//DIDR0 – Digital Input Disable Register 0
//Bits 7:4,2:0 – ADC6D:ADC0D: ADC6:0 Digital Input Disable
//Bit 3 – AREFD: AREF Digital Input Disable
DIDR0 |= ((1<<ADC2D)|(1<<ADC4D)|(1<<ADC5D)|(1<<ADC6D));
// ADATE in ADCSRA - Auto Triggering is enabled by setting
// the ADC Auto Trigger Enable bit
// ADTS in ADCSRB - The trigger source is selected by setting
// the ADC Trigger Select bits
}
| void intInterrupts | ( | void | ) |
Enable Interrupts.
Enables Timer0 Compare A, Timer1 overflow, and ADC end of conversion interrupts
Definition at line 676 of file main.c.
Referenced by main().
{
cli();
//TIMSK – Timer/Counter1 Interrupt Mask Register
//Bit 4 – OCIE0A: Timer/Counter0 Output Compare Match A Interrupt Enable
TIMSK |= (1<<OCIE0A);
//Bit 2 – TOIE1: Timer/Counter1 Overflow Interrupt Enable
TIMSK |= (1<<TOIE1);
//Bit 3 – ADIE: ADC Interrupt Enable
ADCSRA |= (1<<ADIE); //Bit 3 – ADIE: ADC Interrupt Enable
sei();
}
| void intLEDPin | ( | void | ) |
| void intPWM | ( | void | ) |
Initializes Timer 1 as PWM output.
PWM is PWM6 / Dual-slope mode. The ADC conversion is triggered on a PWM reload. PWM is running at 19200Hz. PWM Overflow interrupt is used for UART Communications
Definition at line 466 of file main.c.
References UH, UL, VH, VL, WH, and WL.
Referenced by main().
{
//Clear on up-counting.
TCCR1A = (1 << COM1A1) | (0 << COM1A0) | (1 << PWM1A);
//Set WGM to PWM6, dual slope mode.
TCCR1D = (1 << WGM11) | (1 << WGM10);
// 16MHz/2/19200 = 417 counts, 0 to 416 (pwm + pwm>>1 + pwm>>3 + + pwm>>6)
// (255+127+31+3)
TC1H = 1; //
OCR1C = 0xA0; //416 TOPVALUE (pwm + pwm>>1 + pwm>>3 + + pwm>>6)
// (255+127+31+3)
TCCR1B = (1 << CS10); //Prescaler 16MHz clock /1 = 16MHz
//Set PWM pins as output.
// (PWM output is still controlled through TCCR1E register.)
DDRB = (1 << UL)|(1 << UH)|(1 << VL)|(1 << VH)|(1 << WL)|(1 << WH);
}
| void intTACH | ( | void | ) |
| void intTestPin | ( | void | ) |
Initializes pin for Code testing function.
Test pin is used on a spare pin to look at code timing using an oscilloscope. This is only for Debugging and benchmarking code TEST_PIN_1; and TEST_PIN_0; are inserted in code to set the pin high and low.
Definition at line 400 of file main.c.
References TEST_PIN.
Referenced by main().
| void intTimer0 | ( | void | ) |
Initializes Timer 0 in Normal 16-bit mode.
Timer 0 is used as the frequency generator for the Back EMF sensing Phase Locked Loop (PLL) by load the end count. An interrupt is generated on an Output Compare A Match and a new values is added on to the current value to create an interrupt that is variable frequency for the PLL. The interrupt alternates between triggering a back EMF sample or an update of commutation. The timer uses the 16MHz system clock with a /8 prescaler (2Mhz clock) The frequency of the timer interrupt is proportional to speed.
Definition at line 445 of file main.c.
Referenced by main().
{
// Set up Timer/counter0
TCCR0A = (1<<TCW0); // Normal, 16-bit mode
TCCR0B = (1<<CS01); // Clk/8 Prescaler for 2MHz timer clock
}
| ISR | ( | ADC_vect | ) |
ADC interrupt.
Interrupts on end of conversion. Used to store ADC sample in a variable. The sample alternates between current and Back EMF The back EMF samples are averaged to calculated the neutral point of the motor which is 1/2 the DC bus voltage.
Definition at line 704 of file main.c.
References ADC_MUX_I, adc_mux_table_forward, adc_sample_state, bemf, bemf_ready, bemf_sampled, commutation_step, commutation_step_sampled, current, get_bemf, neutral, neutral_averaging, nr0_temp, pwm, r0_temp, TEST_PIN_0, TEST_PIN_1, v_bus, and zc_table_forward.
{
TEST_PIN_1;
if(adc_sample_state == 0 )
{
current = ADCH;
ADMUX = adc_mux_table_forward[commutation_step];
commutation_step_sampled = commutation_step;
adc_sample_state = 1;
}
else
{
bemf_sampled = ADCH;
if (get_bemf)
{
if (commutation_step_sampled == commutation_step)
{
bemf = bemf_sampled;
neutral_averaging = (neutral_averaging - neutral) + bemf;
v_bus = neutral_averaging>>5;
neutral = v_bus>>1;
if (pwm < 20)
{
// Below 20 counts sample window is too small
v_bus = neutral;
}
bemf_ready = 1;
// Increasing Back EMF
if (zc_table_forward[commutation_step_sampled] == 0)
{
if (bemf <= neutral) // Too Fast
{
r0_temp = 0;
nr0_temp = neutral - bemf;
}
else // Too Slow
{
r0_temp = bemf - neutral;
nr0_temp = 0;
}
}
else
{
if (bemf <= neutral) // Too Slow
{
r0_temp = neutral - bemf;
nr0_temp = 0;
}
else // Too Fast
{
r0_temp = 0;
nr0_temp = bemf - neutral;
}
}
}
get_bemf = 0;
}
ADMUX = ADC_MUX_I;
adc_sample_state = 0;
}
TEST_PIN_0;
}
| ISR | ( | TIMER1_OVF_vect | ) |
Timer 1 Overflow Interrupt.
Interrupt at the beginning of the PWM period. Used as time base for UART communication (Transmit and Receive) also to read in the PWM speed command Also handles counting PWM periods for main control loop timing and includes a UART timeout function so the software UART does not get hung up on interrupted data or if powered up in the middle of a UART byte.
Definition at line 790 of file main.c.
References bit_count_low, bit_position, bit_timeout, byte_timeout, CMD_PIN_1, CMD_PIN_TEST, comm_data, data_in, data_rxd, eeprom_number, EEPROM_TABLE_SIZE, input_state, motor_off, output_state, pwm_cmd, pwm_cmd_next, pwm_cmd_updated, pwm_counter, TACH_PIN_0, TACH_PIN_1, TEST_PIN_0, TEST_PIN_1, valid_byte, variable_table, and VARIABLE_TABLE_SIZE.
{
TEST_PIN_1;
if (CMD_PIN_TEST == CMD_PIN_1)
{
pwm_cmd_next++;
if (bit_position > 0)
{
byte_timeout=0;
bit_timeout++;
if (bit_timeout > 1920) //100ms
{
bit_position = 0;
}
}
else
{
bit_timeout = 0;
}
byte_timeout++;
if((bit_count_low > 6)&&(bit_count_low < 10)) // 7, 8, or 9 PWM periods
{
data_in = 0;
bit_position++;
bit_timeout = 0;
}
if((bit_count_low > 2)&&(bit_count_low < 6)) // 3, 4, or 5 PWM periods
{
data_in = 1;
bit_position++;
bit_timeout = 0;
}
bit_count_low = 0;
}
else
{
bit_count_low++;
if (bit_count_low > 9)
{
bit_count_low = 10;
bit_position = 0;
}
}
if (pwm_counter == 0)
{
pwm_cmd = pwm_cmd_next;
pwm_cmd_next = 0;
pwm_cmd_updated = 1;
}
pwm_counter++;
if (input_state == 1)
{
//Serial Data in (sort of) looking for 4 PWMs low = 1 and
// 8 PWMs low = 0 only when UART receive enabled
if (bit_position == 1)
{
data_rxd = 0; //valid start bit
}
if ((bit_position > 0)&&(bit_position < 9))
{
data_rxd |= data_in<<(bit_position-1);
}
if (bit_position == 8)
{
valid_byte++;
bit_position = 0; //start over
}
if ( byte_timeout >= 19200) //29200/19200Hz = 1sec
{
valid_byte=0;
byte_timeout=0;
}
if (valid_byte == 3)
{
if (eeprom_number < EEPROM_TABLE_SIZE)
{
variable_table[eeprom_number + VARIABLE_TABLE_SIZE] = data_rxd;
}
valid_byte=0;
if ((eeprom_number == 254)&(data_rxd == 254))
{
motor_off = 5;//Motor off command and variables ready for write
}
if ((eeprom_number == 255)&(data_rxd == 255))
{
motor_off = 9; // Motor off command and calibrate oscillator
}
}
if (valid_byte == 1)
{
eeprom_number = data_rxd;
valid_byte++;
}
}
else
{
valid_byte=0;
byte_timeout=0;
}
// Software UART Transmit here
if (output_state == 1)
{
if ((pwm_counter & 0x0F) == 0)
{
TACH_PIN_0; //Start Bit
}
else
{
if ((pwm_counter & 0x0F) < 9)
{
if (comm_data & 0x01) //LSB first
{
TACH_PIN_1; // TXD = 1
}
else
{
TACH_PIN_0; // TXD = 0
}
comm_data = comm_data>>1;
}
else
{
TACH_PIN_1;
}
}
}
TEST_PIN_0;
}
| ISR | ( | TIMER0_COMPA_vect | ) |
Timer 0 Output Compare A Interrupt.
Interrupt on Output Compare Register A The compare value forms the frequency for the back EMF sensing PLL. The interrupt alternates between sampling the back EMF and updating commutation. Updating commutation forms the beginning of the commutation state. The back EMF sample is in the middle of the commutation state, this should be the zero crossing point of the back EMF. This means there are two interrupt per commutation state and six states per commutation cycle (electrical cycle). The tachometer output is also handled in this interrupt.
Definition at line 950 of file main.c.
References comm_inter_state, comm_period, commutation_step, get_bemf, lower_comm_table_forward, output_state, TACH_PIN_0, TACH_PIN_1, TEST_PIN_0, TEST_PIN_1, timer_capture, timer_capture_last, upper_comm_table_forward, and UPPER_DRIVE_MASK.
{
TEST_PIN_1;
if (comm_inter_state == 1) // back EMF sampling
{
get_bemf = 1;
comm_inter_state = 0; // Update Commutation on next interrupt
}
else // update commutation state
{
commutation_step++;
if (commutation_step>=6)
{
commutation_step=0;
}
// Update commutation state
TCCR1E = lower_comm_table_forward[commutation_step];
PORTB &= ~(UPPER_DRIVE_MASK);
PORTB |= upper_comm_table_forward[commutation_step];
if (output_state == 0) //Only update on proper commutation state edge
{
if (commutation_step == 0)
{
TACH_PIN_0;
}
if (commutation_step == 3)
{
TACH_PIN_1;
}
}
comm_inter_state = 1; // Back EMF sample on next interrupt
}
// Update Timer Compare Value
timer_capture = timer_capture_last + comm_period;
OCR0B = (timer_capture >> 8);
OCR0A = timer_capture;
timer_capture_last = timer_capture;
TEST_PIN_0;
}
| int main | ( | void | ) |
Main.
Main handles calling all initialization then sits in an infinite loop. The infinite loop forms the main control loop that is updated every 16 PWM cycles or 833us. The control loop handles all low speed function that do not require interrupt timing, this includes: Next UART byte for transmission Averaging of PWM input (speed command) Updating the back EMF sensing PLL if needed Speed command mapping Update speed loop Current limiting by folding back PWM duty cycle PWM update and various thresholds and house keeping.
Definition at line 1018 of file main.c.
References bemf_error, bemf_ready, bit_test, comm_data, comm_period, comm_period_temp, command, command_0, command_1, command_2, command_3, control_state, count_dir, counter, current, current_limit, current_limit_max, current_limit_slope, current_limit_startup, data_toggle, data_watch, delta, EEPROM_TABLE_SIZE, enable_voltage, HALF_LIMIT, HEADER_SIZE, input_cmd, input_state, intADC(), intercept, intInterrupts(), intLEDPin(), intPWM(), intTACH(), intTestPin(), intTimer0(), ki, kp, led_blink_count, LED_PIN_TOGGLE, LOOP_SCALING, mode, motor_off, nr0, nr0_temp, osc_cal, osc_cal_adj, output_state, pll_gravity, pwm, pwm_cmd, pwm_cmd_filter, pwm_cmd_filtered, pwm_cmd_updated, pwm_counter, pwm_limit, pwm_max, pwm_min, pwm_value, r0, r0_limit, r0_temp, read_eeprom(), s_ki, s_kp, s_loop_count, s_loop_rate, s_nr0, s_r0, s_r0_limit, s_y0, s_y0_max, s_y0_min, s_y1, slope, speed, speed_cmd, speed_cmd_0, speed_cmd_1, speed_cmd_2, speed_cmd_3, speed_cmd_calc, speed_cmd_max, speed_cmd_min, speed_count_max, speed_count_min, speed_error, speed_scale, table_counter, test_oscillator(), v_bus, v_bus_min, v_motor, variable_table, VARIABLE_TABLE_SIZE, write_eeprom(), y0, y0_max, y0_min, and y1.
{
intPWM(); // Initialize timer1 as PWM6 mode center aligned
intTimer0(); // Initialize timer0 for commutation timing
intTACH(); // Initialize I/O pin as tachometer out
intTestPin(); // Initialize I/O pin as test pin
intLEDPin(); // Initialize I/O pin as LED indicator
intADC(); // Initialize ADC
read_eeprom(); // Read the EEPROM contents once on power up
intInterrupts(); // Enable interrupts
osc_cal = OSCCAL; // Read the factory calibration once on power up
while(1)
{
led_blink_count++;
if (led_blink_count >= comm_period_temp)
{
LED_PIN_TOGGLE;
led_blink_count = 0;
}
//Recalculated limits in case they have changed from
// Fan Configuration Utility
y0_min = ((speed_count_min<<LOOP_SCALING) + HALF_LIMIT);
y0_max = ((speed_count_max<<LOOP_SCALING) + HALF_LIMIT);
s_y0_min = ((pwm_min<<LOOP_SCALING) + HALF_LIMIT);
s_y0_max = ((pwm_max<<LOOP_SCALING) + HALF_LIMIT);
// Compare DC input voltage (V_bus) with enable_voltage,
// below threshold use defaults
if (v_bus <= enable_voltage)
{
input_state = 1; //0 = pwm input, 1 = UART Data In
output_state = 1; //0 = Tach output, 1 = UART Data Out
control_state = 1; //0 = Speed Control, 1 = Open Loop PWM
OSCCAL = osc_cal;
}
else
{
input_state = (mode & 0x01); //0 = PWM input,
// 1 = UART Data In
output_state = (mode & 0x02)>>1; //0 = Tach output,
// 1 = UART Data Out
control_state = (mode & 0x04)>>2; //0 = Speed Control,
// 1 = Open Loop PWM
if (osc_cal_adj < 33)
{
OSCCAL = osc_cal + osc_cal_adj - 16;
}
else
{
OSCCAL = osc_cal;
}
}
// Wait here for 16 PWM periods to pass -
// Timing for control loop 19200Hz/16 = 1200Hz (833us)
cli();
bit_test = (pwm_counter & 0x0F);
sei();
while(bit_test != 0x0F)
{
cli();
bit_test = (pwm_counter & 0x0F);
sei();
}
cli();
bit_test = (pwm_counter & 0x0F);
sei();
while(bit_test > 0)
{
cli();
bit_test = (pwm_counter & 0x0F);
sei();
}
// Calculate Motor Voltage
v_motor = (v_bus*pwm)>>8;
// Write EEPROM function here. Non-reentrant so must disable interrupts
if (motor_off == 7) // bit 0 = motor off command,
// bit 1 = motor turned off, bit 2 = transfer variables back to EEPROM,
// bit 3 = Cal Oscillator
{
write_eeprom();
}
if (motor_off == 11) // bit 0 = motor off command,
// bit 1 = motor turned off,
// bit 2 = transfer variables back to EEPROM bit 3 = Cal Oscillator
{
test_oscillator();
}
// Serial Data Selection
if (data_toggle)
{
if (table_counter < (VARIABLE_TABLE_SIZE + EEPROM_TABLE_SIZE))
{
comm_data = variable_table[table_counter];
}
else
{
if (table_counter == (VARIABLE_TABLE_SIZE + EEPROM_TABLE_SIZE))
{
comm_data = 0; // 0 at end of data
}
else
{
comm_data = 255; // The rest of the data is 255s
}
}
table_counter++;
if (table_counter > (HEADER_SIZE + VARIABLE_TABLE_SIZE +
EEPROM_TABLE_SIZE + 1))
{
table_counter = 0;
comm_data = 0; // 0 at beginning of data
}
data_toggle = 0;
}
else
{
if (count_dir == 0)
{
counter++;
if (counter>254)
{
count_dir = 1;
}
}
else
{
counter--;
if (counter<1)
{
count_dir = 0;
}
}
comm_data = variable_table[data_watch];
if (comm_data > 254)
{
comm_data = 254; // Clip data at 254
}
data_toggle = 1;
}
// PWM input (Speed command) averaging filter
if (pwm_cmd_updated == 1)
{
pwm_cmd_updated = 0;
pwm_cmd_filter = (pwm_cmd_filter - pwm_cmd_filtered) + pwm_cmd;
pwm_cmd_filtered = pwm_cmd_filter>>6;
if (pwm_cmd_filtered > 255)
{
pwm_cmd_filtered = 255;
}
}
// Check to see where command comes from either
// Fan Configuration Utility of PWM input (Speed command)
if (input_state == 1)
{
command = input_cmd;
}
else
{
command = pwm_cmd_filtered;
}
// Back EMF Sensing PLL Update
if (bemf_ready == 1)
{
cli();
bemf_ready = 0;
r0=r0_temp;
nr0=nr0_temp;
sei();
// Back EMF sensing PLL PI regulator
if(r0>r0_limit)
{
r0=r0_limit;
}
if(nr0>r0_limit)
{
nr0=r0_limit;
}
nr0 += pll_gravity;
bemf_error = 128 + r0 - nr0;
y1 += ((r0*ki)>>8); // ki
y1 -= ((nr0*ki)>>8); // ki
if (y1 < y0_min)
{
y1 = y0_min;
}
if (y1 > y0_max)
{
y1 = y0_min; // Restart PLL
}
y0 = y1 + ((r0*kp)>>8) - ((nr0*kp)>>8); // kp
if (y0 < y0_min)
{
y0 = y0_min;
}
if (y0 > y0_max)
{
y0 = y0_min; // Restart PLL
}
if (speed_cmd < speed_cmd_min)
{
y1 = y0_min; // Restart PLL
}
speed = (y0 - HALF_LIMIT) >> LOOP_SCALING;
comm_period_temp = (0xFFFF/speed)>>speed_scale;
cli();
comm_period = comm_period_temp;
sei();
}
// Speed Control Loop Update
s_loop_count++;
if (s_loop_count >= s_loop_rate)
{
s_loop_count = 0;
// Speed command Mapping
if (command > command_3)
{
slope = 255 - speed_cmd_3;
delta = command - command_3;
intercept = speed_cmd_3;
}
else
{
if (command > command_2)
{
slope = speed_cmd_3 - speed_cmd_2;
delta = command - command_2;
intercept = speed_cmd_2;
}
else
{
if (command > command_1)
{
slope = speed_cmd_2 - speed_cmd_1;
delta = command - command_1;
intercept = speed_cmd_1;
}
else
{
if (command > command_0)
{
slope = speed_cmd_1 - speed_cmd_0;
delta = command - command_0;
intercept = speed_cmd_0;
}
else
{
slope = 0;
delta = 0;
intercept = 0;
}
}
}
}
speed_cmd_calc = ((slope*delta)/255) + intercept;
// speed_cmd = command; //Linear Mapping for testing purposes
if (speed_cmd_calc > speed_cmd_max)
{
speed_cmd_calc = speed_cmd_max;
}
if (speed_cmd_calc < speed_cmd_min)
{
speed_cmd_calc = speed_cmd_min;
}
speed_cmd = speed_cmd_calc;
// Speed Control PI regulator
if (speed > speed_cmd)
{
s_nr0 = speed - speed_cmd;
s_r0 = 0;
}
else
{
s_r0 = speed_cmd - speed;
s_nr0 = 0;
}
if(s_nr0>s_r0_limit)
{
s_nr0=s_r0_limit;
}
if(s_r0>s_r0_limit)
{
s_r0=s_r0_limit;
}
speed_error = 128 - s_r0 - s_nr0;
s_y1 += ((s_r0*s_ki)>>6); //s_ki
s_y1 -= ((s_nr0*s_ki)>>6); //s_ki
if (s_y1 < s_y0_min)
{
s_y1 = s_y0_min;
}
if (s_y1 > s_y0_max)
{
s_y1 = s_y0_max;
}
s_y0 = s_y1 + ((s_r0*s_kp)>>6) - ((s_nr0*s_kp)>>6); //s_kp
if (s_y0 < s_y0_min)
{
s_y0 = s_y0_min;
}
if (s_y0 > s_y0_max)
{
s_y0 = s_y0_max;
}
pwm = (s_y0 - HALF_LIMIT) >> LOOP_SCALING;
}
// Open Loop PWM or Speed Control
if (control_state == 1) // Direct PWM command for testing purposes
{
if (command>pwm_max)
{
pwm = pwm_max;
}
else
{
pwm = command;
if (pwm < pwm_min)
{
pwm = pwm_min;
}
}
}
// Current limiting
if (speed < speed_cmd_min )
{
// Lower Current limit gives better startup performance
current_limit = current_limit_startup;
}
else //Higher current limit for high speed operation
{
current_limit = current_limit_startup +
((((speed - speed_cmd_min)*current_limit_slope)>>8));
if (current_limit > current_limit_max)
{
// upper current limit for high power operation
current_limit = current_limit_max;
}
}
// Folds back PWM when exceeding current limit
if (current > current_limit)
{
// TEST_PIN_1;
if (pwm_limit > 1)
{
pwm_limit--;
}
}
else
{
if (pwm_limit < pwm)
{
pwm_limit++;
}
}
if (pwm > pwm_limit)
{
pwm = pwm_limit;
}
// Minimum DC Bus Voltage before enabling
// back EMF sensing and speed control
if (v_bus < v_bus_min)
{
pwm = pwm_min;
s_y1 = s_y0_min; // Reset Integrator
s_y0 = 0; // PWM off
y1 = y0_min; // Restart PLL
}
// Motor Off function for updating EEPROM
if (motor_off == 0)
{
// 16MHz/2/19200 = 417 counts, 0 to 416
// (pwm + pwm>>1 + pwm>>3 + + pwm>>6) (255+127+31+3)
pwm_value = pwm + (pwm>>1) + (pwm>>3) + (pwm>>6);
cli();
TC1H = (pwm_value >> 8);
OCR1A = pwm_value;
sei();
}
else
{
cli();
TC1H = 0;
OCR1A = 0;
motor_off |= 2; // Motor PWM at zero
sei();
//pwm = pwm_min;
s_y1 = s_y0_min; // Reset Integrator
s_y0 = 0; // PWM off
y1 = y0_min; // Restart PLL
}
}
}
| void read_eeprom | ( | void | ) |
Reads EEPROM values.
EEPROM values are used for configuration of motor parameters These values are configured and updated from the PC based Fan Configuration Utility
Definition at line 553 of file main.c.
References EEPROM_TABLE_SIZE, table_counter, variable_table, and VARIABLE_TABLE_SIZE.
Referenced by main().
{
table_counter = 0;
while (table_counter < EEPROM_TABLE_SIZE)
{
variable_table[VARIABLE_TABLE_SIZE + table_counter]=
eeprom_read_byte((uint8_t*)(table_counter));
table_counter++;
}
table_counter = 0;
}
| void test_oscillator | ( | void | ) |
Tests accuracy of Oscillator.
Compares to USB to UART bridge oscillator to ATtiny internal oscillator
Definition at line 608 of file main.c.
References CMD_PIN_0, CMD_PIN_1, CMD_PIN_TEST, comm_period, motor_off, osc_error, pin_test, and TEST_PIN_1.
Referenced by main().
{
cli();
//PC sends stream of 0 bytes at 19200 baud, for a 468.75us period
OCR0B = 0xFF;
OCR0A = 0xFF;
//Capture timer period
pin_test = CMD_PIN_TEST;
while (pin_test == CMD_PIN_1) //wait while pin is high
{
pin_test = CMD_PIN_TEST;
}
// TEST_PIN_0;
pin_test = CMD_PIN_TEST;
while (pin_test == CMD_PIN_0) //wait while pin is low
{
pin_test = CMD_PIN_TEST;
}
TEST_PIN_1;
pin_test = CMD_PIN_TEST;
while (pin_test == CMD_PIN_1) //wait while pin is high
{
pin_test = CMD_PIN_TEST;
}
//Capture timer value
TCNT0H = 0;
TCNT0L = 0;
// TEST_PIN_0;
pin_test = CMD_PIN_TEST;
while (pin_test == CMD_PIN_0) //wait while pin is low
{
pin_test = CMD_PIN_TEST;
}
//Capture timer value again
comm_period = TCNT0L;
comm_period += (TCNT0H<<8);
// TEST_PIN_1;
//At 2us per count period and a period of 468.75us the count should be 234.
osc_error = comm_period + 128 - 833;
motor_off = 0;
// TEST_PIN_0;
sei();
}
| void write_eeprom | ( | void | ) |
Writes EEPROM values.
EEPROM values are used for configuration of motor parameters These values are configured and updated from the PC based Fan Configuration Utility
Definition at line 580 of file main.c.
References EEPROM_TABLE_SIZE, motor_off, table_counter, variable_table, and VARIABLE_TABLE_SIZE.
Referenced by main().
{
cli();
table_counter = 0;
while (table_counter < EEPROM_TABLE_SIZE)
{
eeprom_write_byte ((uint8_t*)(table_counter),
variable_table[VARIABLE_TABLE_SIZE + table_counter]);
table_counter++;
}
table_counter = 0;
// update_limits();
motor_off = 0;
sei();
}
| uint8_t adc_mux_table_forward[6] |
| uint8_t adc_sample |
| uint8_t adc_sample_state = 0 |
| uint8_t bemf_ready = 0 |
| uint8_t bemf_sampled |
| uint8_t bit_count_low |
| uint8_t bit_position |
| uint16_t bit_timeout |
| uint16_t byte_timeout |
| uint8_t comm_inter_state |
| uint16_t comm_period |
Definition at line 203 of file main.c.
Referenced by ISR(), main(), and test_oscillator().
| uint16_t comm_period_temp |
| uint8_t commutation_step = 0 |
| uint8_t commutation_step_sampled = 0 |
| uint8_t control_state = 1 |
| uint8_t data_toggle |
| uint8_t eeprom_number |
| uint8_t input_state = 1 |
| uint16_t led_blink_count = 0 |
| uint8_t lower_comm_table_forward[6] |
{
LOWER_DRIVE_STEP2_CW,
LOWER_DRIVE_STEP1_CW,
LOWER_DRIVE_STEP6_CW,
LOWER_DRIVE_STEP5_CW,
LOWER_DRIVE_STEP4_CW,
LOWER_DRIVE_STEP3_CW
}
Definition at line 165 of file main.c.
Referenced by ISR().
| uint8_t motor_max_speed |
| uint8_t motor_min_speed |
| uint8_t motor_norm_speed |
| uint8_t motor_off |
Definition at line 287 of file main.c.
Referenced by ISR(), main(), test_oscillator(), and write_eeprom().
| uint16_t neutral_averaging |
| uint8_t output_state = 1 |
| uint8_t pin_test |
Definition at line 289 of file main.c.
Referenced by test_oscillator().
| uint16_t pwm_cmd_filter |
| uint16_t pwm_cmd_filtered |
| uint16_t pwm_cmd_next |
| uint8_t pwm_cmd_updated |
| uint8_t pwm_counter = 0 |
| uint8_t s_loop_count = 0 |
| uint16_t speed_cmd_calc |
| uint8_t table_counter = 0 |
Definition at line 295 of file main.c.
Referenced by main(), read_eeprom(), and write_eeprom().
| uint16_t timer_capture |
| uint16_t timer_capture_last |
| uint8_t upper_comm_table_forward[6] |
{
UPPER_DRIVE_STEP2_CW,
UPPER_DRIVE_STEP1_CW,
UPPER_DRIVE_STEP6_CW,
UPPER_DRIVE_STEP5_CW,
UPPER_DRIVE_STEP4_CW,
UPPER_DRIVE_STEP3_CW
}
Definition at line 153 of file main.c.
Referenced by ISR().
| uint8_t valid_byte |
Definition at line 298 of file main.c.
Referenced by ISR(), main(), read_eeprom(), and write_eeprom().
| uint8_t zc_table_forward[6] |
{
EDGE_RISING,
EDGE_FALLING,
EDGE_RISING,
EDGE_FALLING,
EDGE_RISING,
EDGE_FALLING
}
Definition at line 177 of file main.c.
Referenced by ISR().